//////////////////////////////////////////////////////////////////////////
///	\file:	SocketWriter.cpp
///	Copyright (MingYou) 2009 - All Rights Reserved
///	Author:	Robert.Ma
///	Date:	[2009/05/14]
///	Description:
///		
//////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "SocketWriter.h"
#include "InlineFunction.h"
#include "CETool.h"
#include "Define.h"


CSocketWriter::CSocketWriter(void)
		: m_hMutex(NULL),
		m_pSocket(NULL),
		m_nSocketError(0),
		m_dwParentID(ULONG_MAX)
{
}

CSocketWriter::~CSocketWriter(void)
{
	Release();
}

BOOL CSocketWriter::Initialize(SOCKET* pSocket, DWORD dwParentID)
{
	//////////////////////////////////////////////////////////////////////////
	// Create thread...

	m_pSocket = pSocket;
	m_nSocketError = 0;
	m_dwParentID = dwParentID;

	return (NULL != CMThread::Create());
}

void CSocketWriter::Release()
{
	//	Close thread firstly, or maybe cause an error.
	CMThread::Close();

	::ErasePoiter(m_pSocket);
	m_nSocketError = 0;
	ClearBuffer();
}

int CSocketWriter::SocketWrite(BYTE* pData, DWORD dwLen)
{
	int iDataPos	= 0;
	int iSendLen	= 0;

	ASSERT(NULL != m_pSocket);

	while ( 0 < dwLen )
	{
		iSendLen = ::send(*m_pSocket, (const char*)(&(pData[iDataPos])), dwLen, 0);

		if ( SOCKET_ERROR == iSendLen )
		{
			m_nSocketError = WSAGetLastError();
			SafePostMessage(m_dwParentID, TM_SOCKET_ERROR, m_nSocketError);

			return SOCKET_ERROR;
		}

		iDataPos += iSendLen;
		dwLen -= iSendLen;
	}

	return iSendLen;
}

DWORD CSocketWriter::Execute()
{
	MSG msg;

	while ( ::GetMessage(&msg, NULL, 0, 0) )
	{
		switch ( msg.message )
		{
		case TM_SEND_TASK_START:
			if ( EnterBuffer() )
			{
				SocketSendBuffer();

				LeaveBuffer();
			}
			break;

		case TM_THREAD_EXIT:
			//	Do some clear...
			//MTRACE(L"CSocketWriter thread exit.");
			return 0;

		default:
			ASSERT(FALSE);
			break;
		}
	}

	return 0;
}

void CSocketWriter::ClearBuffer()
{
	int iCounts = 0;
	while ( EnterBuffer() )
	{
		if ( 5 < ++iCounts )
		{
			//	Clear buffer failed, we can clear it next time.
			return;
		}
	}

	DataPacket* pPack = NULL;
	std::list<DataPacket*>::iterator iter;

	while ( m_SendBuffer.size() > 0 )
	{
		iter = m_SendBuffer.begin();
		pPack = *(iter);
		m_SendBuffer.erase(iter);

		FreePacket(pPack);
	}

	LeaveBuffer();
}

void CSocketWriter::FreePacket(DataPacket* pPack)
{
	BYTE* pData = pPack->pBuf;

	::ClearArrayPointer(pData);
	::ClearPointer(pPack);
}

void CSocketWriter::SocketSendBuffer()
{
	DataPacket* pPack = NULL;
	std::list<DataPacket*>::iterator iter;

	while ( m_SendBuffer.size() > 0 )
	{
		iter = m_SendBuffer.begin();
		pPack = *(iter);
		//	Send packet to server.
		SocketWrite(pPack->pBuf, pPack->iLen);

		FreePacket(pPack);

		m_SendBuffer.erase(iter);
	}
}

int CSocketWriter::Write(const BYTE *pData, DWORD dwLen)
{
	int iResult = -1;

	if ( EnterBuffer() )
	{
		DataPacket* pPack = new DataPacket;
		pPack->iLen = dwLen;
		pPack->pBuf = new BYTE[dwLen];
		::memcpy_s(pPack->pBuf, pPack->iLen, pData, dwLen);

		m_SendBuffer.push_back(pPack);
		LeaveBuffer();

		SafePostMessage(GetThreadID(), TM_SEND_TASK_START);

		iResult = 0;
	}

	return iResult;
}

BOOL CSocketWriter::EnterBuffer()
{
	return (WAIT_OBJECT_0 == ::WaitForSingleObject(m_hMutex, WAITBUFFER_TIME));
}

void CSocketWriter::LeaveBuffer()
{
	::ReleaseMutex(m_hMutex);
}

void CSocketWriter::PreClose()
{
	SafePostMessage(GetThreadID(), TM_THREAD_EXIT);
}

void CSocketWriter::AfterClose()
{
	if ( NULL != m_hMutex )
	{
		::CloseHandle(m_hMutex);
	}
}

void CSocketWriter::PreCreate()
{
	if ( NULL == m_hMutex )
	{
		m_hMutex = ::CreateMutex(NULL, FALSE, L"CSocketWriter Mutex");
	}
}

void CSocketWriter::AfterCreate()
{}
